package com.xinqi.modules.chat.favorite.service.impl;

import cn.hutool.core.convert.Convert;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.xinqi.common.base.enums.YesNoNumberEnum;
import com.xinqi.common.base.error.CommonErrors;
import com.xinqi.common.base.response.PageInfo;
import com.xinqi.common.base.response.Param;
import com.xinqi.common.core.constant.Constants;
import com.xinqi.common.core.enums.FavoriteLimitEnum;
import com.xinqi.common.core.enums.FavoriteSourceTypeEnum;
import com.xinqi.common.core.utils.MessageUtils;
import com.xinqi.common.core.utils.StringUtils;
import com.xinqi.common.mybatis.data.service.impl.BaseServiceImpl;
import com.xinqi.common.mybatis.utils.Pages;
import com.xinqi.common.satoken.utils.LoginHelper;
import com.xinqi.modules.chat.favorite.convert.FavoriteContentConvert;
import com.xinqi.modules.chat.favorite.convert.FavoriteTagConvert;
import com.xinqi.modules.chat.favorite.domain.FavoriteContentEntity;
import com.xinqi.modules.chat.favorite.domain.FavoriteTagEntity;
import com.xinqi.modules.chat.favorite.dto.req.CollectContentDTO;
import com.xinqi.modules.chat.favorite.dto.req.CollectDeleteBatchTagDTO;
import com.xinqi.modules.chat.favorite.dto.req.CollectDeleteTagDTO;
import com.xinqi.modules.chat.favorite.dto.req.CollectEditBatchTagDTO;
import com.xinqi.modules.chat.favorite.dto.req.CollectEditTagDTO;
import com.xinqi.modules.chat.favorite.dto.req.FavoriteContentCreateDTO;
import com.xinqi.modules.chat.favorite.dto.req.FavoriteContentDeleteDTO;
import com.xinqi.modules.chat.favorite.dto.req.FavoriteContentQueryDTO;
import com.xinqi.modules.chat.favorite.dto.rsp.FavoriteContentResultDTO;
import com.xinqi.modules.chat.favorite.dto.rsp.FavoriteTagResultDTO;
import com.xinqi.modules.chat.favorite.mapper.FavoriteContentMapper;
import com.xinqi.modules.chat.favorite.service.FavoriteContentService;
import com.xinqi.modules.chat.favorite.service.FavoriteTagService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 收藏内容表服务层实现
 *
 * @author: yyzhen
 * @date: 2023/03/23
 */
@Service("favoriteContentService")
@RequiredArgsConstructor
public class FavoriteContentServiceImpl extends BaseServiceImpl<FavoriteContentMapper, FavoriteContentEntity> implements FavoriteContentService {

    private final FavoriteContentConvert favoriteContentConvert;

    private final FavoriteTagConvert favoriteTagConvert;

    private final FavoriteTagService favoriteTagService;

    /**
     * 收藏
     *
     * @Title:
     * @MethodName: collection
     * [dto]
     * @Return java.lang.Boolean
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/3/23 14:41
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<FavoriteContentResultDTO> collection(CollectContentDTO dto) {

        // 校验标签是否超过可添加的最大数量
        if (dto.getTagTitles() != null) {
            CommonErrors.BAD_REQUEST.check(dto.getTagTitles().size() <= FavoriteLimitEnum.MAX_ASSIGN.getNumber(), MessageUtils.message("chat.favorite.tag.assign.valid"));
        }

        this.checkSourceType(dto.getList());

        // 绑定收藏用户ID
        dto.setOperationUserId(LoginHelper.getUserId());

        // 多标签获取 (初始化标签为0)
        String tagIds = "0";
        if (dto.getTagTitles() != null && dto.getTagTitles().size() > 0) {
            List<Long> tagIdList = favoriteTagService.checkByTitlesAndUserId(dto.getTagTitles(), dto.getOperationUserId());
            tagIds = tagIdList.stream().map(Convert::toStr).collect(Collectors.joining(","));
        }

        // 收藏内容
        List<FavoriteContentCreateDTO> newLCreateList = new ArrayList<>();
        // 新收藏内容
        List<FavoriteContentEntity> newList = new ArrayList<>();
        // 已收藏内容
        List<FavoriteContentEntity> oldList = new ArrayList<>();

        try {
            // 检索新收藏和已收藏数据
            if (dto.getList() != null && dto.getList().size() > 0) {
                // 初始化新的收藏内容
                newLCreateList = dto.getList();
                // 去除由Id的内容
                Iterator<FavoriteContentCreateDTO> iterator = newLCreateList.iterator();
                while (iterator.hasNext()) {
                    FavoriteContentCreateDTO item = iterator.next();
                    if (Objects.nonNull(item.getId())) {
                        FavoriteContentEntity entity = favoriteContentConvert.create(item);
                        oldList.add(entity);
                        iterator.remove();
                    }
                }
                // 校验是否是重复收藏内容
                oldList.addAll(this.baseMapper.selectFavoriteContentList(newLCreateList, dto.getOperationUserId()));

                // 校验新收藏内容
                if (oldList.size() > 0) {
                    for (FavoriteContentEntity entity : oldList) {
                        // 已收藏的内容修改标签
                        entity.setTagIds(tagIds);
                        // 根据不同的消息来源检索新收藏内容信息
                        for (FavoriteContentCreateDTO createDto : newLCreateList) {
                            // 收藏来源聊天只需判断 来源ID是否相同
                            if (createDto.getSourceType().equals(FavoriteSourceTypeEnum.CHAT.getCode())
                                && createDto.getSourceId().equals(entity.getSourceId())) {
                                newLCreateList.remove(createDto);
                                break;
                            }
                            // 收藏来源为非聊天需判断 来源ID是否相同 文件路径是否相同
                            if (!createDto.getSourceType().equals(FavoriteSourceTypeEnum.CHAT.getCode())
                                && createDto.getSourceId().equals(entity.getSourceId())
                                && createDto.getFileUrl().equals(entity.getFileUrl())) {
                                newLCreateList.remove(createDto);
                                break;
                            }
                        }
                    }
                }

            }

            // 新收藏内容校验内容基本信息
            for (FavoriteContentCreateDTO item : newLCreateList) {
                // 设置资源大小
                if (Objects.isNull(item.getSize())) {
                    if (StringUtils.isNotEmpty(item.getFileUrl())) {
                        String fileUrl = item.getFileUrl();
                        long fileSize = getResourceLength(fileUrl);
                        item.setSize(fileSize);
                    }
                }
                // 收藏类型：3--文档、6--压缩文件 添加图标值
                if ((item.getTypeId() == 3 || item.getTypeId() == 6) && StringUtils.isNotEmpty(item.getFileUrl())) {
                    String ext = item.getContent().substring(item.getContent().lastIndexOf(".") + 1).toLowerCase();
                    if (Objects.equals(Constants.DOC_SUF, ext) || Objects.equals(Constants.DOCX_SUF, ext)) {
                        item.setFileExt(Constants.DOC_COL);
                    } else if (Objects.equals(Constants.PPT_SUF, ext) || Objects.equals(Constants.PPTX_SUF, ext)
                        || Objects.equals(Constants.PDF_SUF, ext)) {
                        item.setFileExt(Constants.PPT_COL);
                    } else if (Objects.equals(Constants.XLS_SUF, ext) || Objects.equals(Constants.XLSX_SUF, ext)) {
                        item.setFileExt(Constants.XLS_COL);
                    } else if (Objects.equals(Constants.ODT_SUF, ext) || Objects.equals(Constants.ODTX_SUF, ext)) {
                        item.setFileExt(Constants.ODT_COL);
                    } else if (Objects.equals(Constants.RAR_SUF, ext) || Objects.equals(Constants.ZIP_SUF, ext)) {
                        item.setFileExt(Constants.RAR_COL);
                    } else if (Objects.equals(Constants.TXT_SUF, ext)) {
                        item.setFileExt(Constants.TXT_COL);
                    } else {
                        item.setFileExt(Constants.UNKNOW_COL);
                    }
                }
                FavoriteContentEntity entity = favoriteContentConvert.create(item);
                entity.setTagIds(tagIds);
                entity.setUserId(dto.getOperationUserId());
                newList.add(entity);
            }


        } catch (Exception e) {
            log.error("收藏失败，获取url地址资源大小异常", e);
            return new ArrayList<>();
        }

        List<Long> idList = new ArrayList<>();
        // 保存新收藏内容信息
        if (CollectionUtils.isNotEmpty(newList)) {
            this.saveBatch(newList);
            List<Long> ids = newList.stream().map(FavoriteContentEntity::getId).collect(Collectors.toList());
            idList.addAll(ids);
        }

        // 更新已收藏内容信息
        if (CollectionUtils.isNotEmpty(oldList)) {
            this.updateBatchById(oldList);
            List<Long> ids = oldList.stream().map(FavoriteContentEntity::getId).collect(Collectors.toList());
            idList.addAll(ids);
        }
        LambdaQueryWrapper<FavoriteContentEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(FavoriteContentEntity::getId, idList);
        return this.list(wrapper).stream().map(favoriteContentConvert::convert).collect(Collectors.toList());
    }

    /**
     * 校验来源类型是否正确
     */
    private void checkSourceType(List<FavoriteContentCreateDTO> list) {
        for (FavoriteContentCreateDTO create : list) {
            // 判断未保存的数据
            if (Objects.isNull(create.getId())) {
                if (create.getSourceType() > 11 || create.getSourceType() == 0) {
                    CommonErrors.BAD_REQUEST.check(false, "收藏内容来源类型错误");
                }
            }
        }
    }

    /**
     * 收藏 -- 修改标签
     *
     * @Title:
     * @MethodName: editTag
     * [dto]
     * @Return java.lang.Boolean
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/3/23 15:03
     */
    @Override
    public Boolean editTag(CollectEditTagDTO dto) {

        // 校验标签是否超过可添加的最大数量
        if (dto.getTagTitles() != null) {
            CommonErrors.BAD_REQUEST.check(dto.getTagTitles().size() <= FavoriteLimitEnum.MAX_ASSIGN.getNumber(), MessageUtils.message("chat.favorite.tag.assign.valid"));
        }
        // 多标签校验获取主键集合
        String tagIds = null;
        if (dto.getTagTitles() != null && dto.getTagTitles().size() > 0) {
            List<Long> tagIdList = favoriteTagService.checkByTitlesAndUserId(dto.getTagTitles(), dto.getOperationUserId());
            tagIds = tagIdList.stream().map(Convert::toStr).collect(Collectors.joining(","));
        }

        LambdaUpdateWrapper<FavoriteContentEntity> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(FavoriteContentEntity::getTagIds, tagIds)
            .set(FavoriteContentEntity::getUpdateTime, LocalDateTime.now())
            .eq(FavoriteContentEntity::getId, dto.getId())
            .eq(FavoriteContentEntity::getUserId, dto.getOperationUserId());

        return this.update(wrapper);
    }

    /**
     * 收藏 -- 多选 -- 添加标签
     *
     * @Title:
     * @MethodName: editBatchTag
     * [dto]
     * @Return java.lang.Boolean
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/3/23 15:17
     */
    @Override
    public Boolean editBatchTag(CollectEditBatchTagDTO dto) {
        //获取标签
        Long tagId = favoriteTagService.checkByTitleAndUserId(dto.getTagTitle(), dto.getOperationUserId());
        LambdaUpdateWrapper<FavoriteContentEntity> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(FavoriteContentEntity::getTagId, tagId)
            .in(FavoriteContentEntity::getId, dto.getIds())
            .eq(FavoriteContentEntity::getUserId, dto.getOperationUserId());

        return this.update(wrapper);
    }

    /**
     * 收藏 -- 删除标签
     *
     * @Title:
     * @MethodName: delTag
     * [dto]
     * @Return java.lang.Boolean
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/3/23 15:25
     */
    @Override
    public Boolean delTag(CollectDeleteTagDTO dto) {
        FavoriteContentEntity old = Optional.ofNullable(this.getById(dto.getId()))
            .orElseThrow(() -> CommonErrors.BAD_REQUEST.asException(MessageUtils.message("data.does.not.exist")));
        CommonErrors.BAD_REQUEST.check(Objects.equals(dto.getOperationUserId(), old.getUserId()), MessageUtils.message("chat.favorite.content.edit.tag.fail"));

        LambdaUpdateWrapper<FavoriteContentEntity> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(FavoriteContentEntity::getTagIds, null)
            .eq(FavoriteContentEntity::getId, dto.getId())
            .eq(FavoriteContentEntity::getUserId, dto.getOperationUserId());

        return this.update(wrapper);
    }

    /**
     * 收藏 -- 多选 -- 删除标签
     *
     * @Title:
     * @MethodName: delTag
     * [dto]
     * @Return java.lang.Boolean
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/3/23 15:25
     */
    @Override
    public Boolean delBatchTag(CollectDeleteBatchTagDTO dto) {
        LambdaUpdateWrapper<FavoriteContentEntity> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(FavoriteContentEntity::getTagIds, null)
            .in(FavoriteContentEntity::getId, dto.getIds())
            .eq(FavoriteContentEntity::getUserId, dto.getOperationUserId());

        return this.update(wrapper);
    }

    /**
     * 收藏 -- 删除
     *
     * @Title:
     * @MethodName: removeById
     * [id , operationUserId]
     * @Return java.lang.Boolean
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/3/23 15:31
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean removeById(Long id, Long operationUserId) {
        FavoriteContentEntity old = Optional.ofNullable(this.getById(id))
            .orElseThrow(() -> CommonErrors.BAD_REQUEST.asException(MessageUtils.message("data.does.not.exist")));
        CommonErrors.BAD_REQUEST.check(Objects.equals(operationUserId, old.getUserId()), MessageUtils.message("chat.favorite.content.remove.fail"));

        return this.deleteById(id);
    }

    /**
     * 收藏 -- 多选 -- 删除
     *
     * @Title:
     * @MethodName: removeByIds
     * [dto]
     * @Return java.lang.Boolean
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/3/23 15:31
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean removeByIds(FavoriteContentDeleteDTO dto) {
        return this.deletedByIds(dto.getIds());
    }

    /**
     * 收藏 -- 分页列表
     *
     * @Title:
     * @MethodName: selectPage
     * [pageable, query]
     * @Return com.xinqi.common.base.response.PageInfo<com.xinqi.modules.chat.favorite.dto.rsp.FavoriteContentResultDTO>
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/3/23 16:58
     */
    @Override
    @DS("slaver")
    public PageInfo<FavoriteContentResultDTO> selectPage(Param pageable, FavoriteContentQueryDTO query) {
        //封装排序
        pageable.setOrderByColumn(StringUtils.EMPTY);
        pageable.setIsAsc(StringUtils.EMPTY);
        pageable.setSorts(new ArrayList<>());
        //最近查看列表
        if (Objects.equals(YesNoNumberEnum.YES.getCode(), query.getRecent())) {
            //默认排序  最近查看时间倒序
            pageable.getSorts().add("t1.recent_view_time,DESC");
        } else if (Objects.isNull(query.getOrderBy())) {
            //默认排序
            pageable.getSorts().add("t1.create_time,DESC");
        } else if (1 == query.getOrderBy()) {
            //1 -- 按修改时间
            pageable.getSorts().add("t1.update_time,DESC");
        } else if (2 == query.getOrderBy()) {
            //2 -- 按文件名
            pageable.getSorts().add("CONVERT ( t1.content USING gbk ) COLLATE gbk_chinese_ci,ASC");
        } else if (3 == query.getOrderBy()) {
            //3 -- 按文件大小
            pageable.getSorts().add("t1.size,DESC");
        } else {
            //默认排序
            pageable.getSorts().add("t1.create_time,DESC");
        }

        IPage<FavoriteContentResultDTO> page = this.baseMapper.selectPage1(Pages.page(pageable), query);

        // 获取List涉及的所有标签
        List<Long> tagIdList = new ArrayList<>();
        for (FavoriteContentResultDTO resultDTO : page.getRecords()) {
            if (StringUtils.isNotEmpty(resultDTO.getTagIds())) {
                String[] tagIds = resultDTO.getTagIds().split(",");
                tagIdList.addAll(Arrays.stream(tagIds).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList()));
            }
        }
        List<FavoriteTagResultDTO> allTagList = new ArrayList<>();
        if (tagIdList.size() > 0) {
            LambdaUpdateWrapper<FavoriteTagEntity> tagWrapper = new LambdaUpdateWrapper<>();
            tagWrapper.in(FavoriteTagEntity::getId, tagIdList);
            allTagList = favoriteTagConvert.convert(favoriteTagService.list(tagWrapper));
        }


        // 给收藏内容付标签
        if (allTagList.size() > 0) {
            for (FavoriteContentResultDTO resultDTO : page.getRecords()) {
                if (StringUtils.isNotEmpty(resultDTO.getTagIds())) {
                    String[] tagIds = resultDTO.getTagIds().split(",");
                    List<Long> favoriteTagIds = Arrays.stream(tagIds).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList());
                    List<FavoriteTagResultDTO> tagList = allTagList.stream().filter(t -> favoriteTagIds.contains(t.getId())).collect(Collectors.toList());
                    resultDTO.setTagList(tagList.stream().map(FavoriteTagResultDTO::getTitle).collect(Collectors.toList()));
                }
            }
        }

        return Pages.convert(page);
    }

    /**
     * 收藏 --APP分页列表
     *
     * @param pageable
     * @param query
     * @return
     */
    @Override
    @DS("slaver")
    public PageInfo<FavoriteContentResultDTO> selectAppPage(Param pageable, FavoriteContentQueryDTO query) {
        //封装排序
        pageable.setOrderByColumn(StringUtils.EMPTY);
        pageable.setIsAsc(StringUtils.EMPTY);
        pageable.setSorts(new ArrayList<>());
        //最近查看列表
        if (Objects.equals(YesNoNumberEnum.YES.getCode(), query.getRecent())) {
            //默认排序  最近查看时间倒序
            pageable.getSorts().add("t1.recent_view_time,DESC");
        } else {
            //默认排序
            pageable.getSorts().add("sort_time,DESC");
        }

        IPage<FavoriteContentResultDTO> page = this.baseMapper.selectPage1(Pages.page(pageable), query);

        // 获取List涉及的所有标签
        List<Long> tagIdList = new ArrayList<>();
        for (FavoriteContentResultDTO resultDTO : page.getRecords()) {
            // 判断如果更新时间为空 APP端显示 创建时间 否侧 显示更新时间
            resultDTO.setCreateTime(resultDTO.getUpdateTime() == null ? resultDTO.getCreateTime() : resultDTO.getUpdateTime());
            if (StringUtils.isNotEmpty(resultDTO.getTagIds())) {
                String[] tagIds = resultDTO.getTagIds().split(",");
                tagIdList.addAll(Arrays.stream(tagIds).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList()));
            }
        }
        List<FavoriteTagResultDTO> allTagList = new ArrayList<>();
        if (tagIdList.size() > 0) {
            LambdaUpdateWrapper<FavoriteTagEntity> tagWrapper = new LambdaUpdateWrapper<>();
            tagWrapper.in(FavoriteTagEntity::getId, tagIdList);
            allTagList = favoriteTagConvert.convert(favoriteTagService.list(tagWrapper));
        }


        // 给收藏内容付标签
        if (allTagList.size() > 0) {
            for (FavoriteContentResultDTO resultDTO : page.getRecords()) {
                if (StringUtils.isNotEmpty(resultDTO.getTagIds())) {
                    String[] tagIds = resultDTO.getTagIds().split(",");
                    List<Long> favoriteTagIds = Arrays.stream(tagIds).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList());
                    List<FavoriteTagResultDTO> tagList = allTagList.stream().filter(t -> favoriteTagIds.contains(t.getId())).collect(Collectors.toList());
                    resultDTO.setTagList(tagList.stream().map(FavoriteTagResultDTO::getTitle).collect(Collectors.toList()));
                }
            }
        }

        return Pages.convert(page);
    }

    /**
     * 查看收藏详情（当前用户为收藏拥有着则修改最近查看时间）
     *
     * @Title:
     * @MethodName: selectById
     * [id, operationUserId]
     * @Return com.xinqi.modules.chat.favorite.dto.rsp.FavoriteContentResultDTO
     * @Exception
     * @Description:
     * @author: yyzhen
     * @date: 2023/3/23 16:58
     */
    @Override
    public FavoriteContentResultDTO selectById(Long id, Long operationUserId) {

        FavoriteContentResultDTO resultDTO = Optional.ofNullable(this.baseMapper.selectFavoriteContentById(id))
            .orElseThrow(() -> CommonErrors.BAD_REQUEST.asException(MessageUtils.message("data.does.not.exist")));

        if (resultDTO.getTagIds() != null) {
            LambdaUpdateWrapper<FavoriteTagEntity> tagWrapper = new LambdaUpdateWrapper<>();
            tagWrapper.in(FavoriteTagEntity::getId, resultDTO.getTagIds().split(","));
            List<FavoriteTagResultDTO> tagList = favoriteTagConvert.convert(favoriteTagService.list(tagWrapper));
            resultDTO.setTagList(tagList.stream().map(FavoriteTagResultDTO::getTitle).collect(Collectors.toList()));
        }

        if (Objects.equals(operationUserId, resultDTO.getUserId())) {
            //最近查看
            LambdaUpdateWrapper<FavoriteContentEntity> wrapper = new LambdaUpdateWrapper<>();
            wrapper.set(FavoriteContentEntity::getRecentViewTime, LocalDateTime.now())
                .in(FavoriteContentEntity::getId, id);
            this.update(wrapper);
        }

        return resultDTO;
    }

    /**
     * 给定url地址获取资源的大小（以kB为单位）
     *
     * @param urlStr
     * @return
     * @throws IOException
     */
    private long getResourceLength(String urlStr) throws IOException {
        URL url = new URL(urlStr);
        URLConnection urlConnection = url.openConnection();
        urlConnection.connect();
        //返回响应报文头字段Content-Length的值
        return urlConnection.getContentLength() / 1024;
    }

}
